package com.buddy.learn.spring.framework.aop.support;

import com.buddy.learn.spring.framework.aop.aspect.BAfterAdviceInterceptor;
import com.buddy.learn.spring.framework.aop.aspect.BAfterThrowingAdviceInterceptor;
import com.buddy.learn.spring.framework.aop.aspect.BMethodBeforeAdviceInterceptor;
import com.buddy.learn.spring.framework.aop.config.BAopConfig;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BAdvisedSupport {

    private Class<?> targetClass;
    private Object target;

    private BAopConfig config;

    private Pattern pointCutClassPattern;

    private Map<Method,List<Object>> methodCache;

    public BAdvisedSupport(BAopConfig config) {

        //解析配置文件
        this.config = config;
    }

    //获取拦截器链
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) throws NoSuchMethodException {
        List<Object> cached = methodCache.get(method);
        if (cached == null){
            //没有拦截器链
           Method m =  targetClass.getMethod(method.getName(),method.getParameterTypes());
           //底层逻辑，对代理方法进行兼容处理
           cached = methodCache.get(m);
            this.methodCache.put(m,cached);
        }
        return cached;
    }

    public Class<?> getTargetClass() {
        return this.targetClass;
    }

    public Object getTarget() {
        return target;
    }

    public void setTargetClass(Class<?> targetClass) {
        this.targetClass = targetClass;
        parse();
    }

    private void parse() {
        //public .* com.buddy.learn.spring.demo.service..*Service..*(.*)
        String pointCut = config.getPointCut().replaceAll("\\.", "\\\\.")
                .replaceAll("\\\\.\\*", ".*")
                .replaceAll("\\(", "\\\\(")
                .replaceAll("\\)", "\\\\)");
        //提取类名
        String pointCutForClassRegx = pointCut.substring(0, pointCut.lastIndexOf("\\\\(") - 4);

        pointCutClassPattern = Pattern.compile("class " + pointCutForClassRegx
                .substring(pointCutForClassRegx.lastIndexOf(" " + 1)));//截取public 后的


        try {

            methodCache = new HashMap<>();

            Pattern pattern = Pattern.compile(pointCut);

            Class aspectClass = Class.forName(this.config.getAspectClass());
            Map<String, Method> aspectMethods = new HashMap<>();
            for (Method method : aspectClass.getMethods()) {
                aspectMethods.put(method.getName(), method);
            }


            for (Method method : this.targetClass.getMethods()) {
                //获取类的所有方法
                String methodString = method.toString();
                if (methodString.contains("throws")) {
                    //如果方法匹配到异常
                    methodString = methodString.substring(0, methodString.lastIndexOf("throws")).trim();

                }
                Matcher matcher = pattern.matcher(methodString);
                if (matcher.matches()) {
                    //给某个方法封装拦截器链
                    //执行器链
                    List<Object> advices = new LinkedList<>();
                    //如果方法名能够匹配上，把方法包装成MethodInterceptor
                    //1.before
                    if (!(null == config.getAspectBefore() || "".equals(config.getAspectBefore()))) {
                        //创建一个advice对象
                        advices.add(new BMethodBeforeAdviceInterceptor(aspectMethods.get(config.getAspectBefore()), aspectClass.newInstance()));
                    }
                    //2.after
                    if (!(null == config.getAsepctAfter() || "".equals(config.getAsepctAfter()))) {
                        //创建一个advice对象
                        advices.add(new BAfterAdviceInterceptor(aspectMethods.get(config.getAsepctAfter()), aspectClass.newInstance()));
                    }
                    //3.afterThrowing
                    if (!(null == config.getAspectAfterThrow() || "".equals(config.getAspectAfterThrow()))) {
                        //创建一个advice对象
                        BAfterThrowingAdviceInterceptor afterThrowingAdviceInterceptor =
                                new BAfterThrowingAdviceInterceptor(
                                        aspectMethods.get(config.getAspectAfterThrow()),
                                        aspectClass.newInstance());
                        afterThrowingAdviceInterceptor.setThrowName(config.getAspectAfterThrowingName());
                        advices.add(afterThrowingAdviceInterceptor);

                    }
                    methodCache.put(method,advices);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    public void setTarget(Object target) {
        this.target = target;
    }

    /**
     * 使用正则判断
     *
     * @return
     */
    public boolean pointCutMatch() {

        return pointCutClassPattern.matcher(this.target.toString()).matches();
    }
}
